"""Sign-in inside Cinema 4D: device flow on a worker thread + verdict cache.

The Blender twin is a modal operator draining a queue on a timer; here the
worker pushes through `events` and the dialog's `CoreMessage` drains on the
main thread — same `_core.api.login_flow`, different plumbing (plan §4).

The dialog reads `STATE` (idle | waiting | error) and `VERDICT` (the last
/api/tools/validate response) from here.
"""

from __future__ import annotations

import threading

from . import events, host
from ._core import api, config

# The account card's login status. phase: "idle" | "waiting" | "error".
STATE = {"phase": "idle", "message": ""}

# Last validate response ({entitled, plan, planDisplay, …}) or None. In-memory
# only — the card shows it when known, says nothing when not.
VERDICT = None

_stop = threading.Event()


def set_verdict(verdict) -> None:
    global VERDICT
    VERDICT = verdict


def start_login() -> tuple[bool, str]:
    """Begin the device flow; events arrive as ("login", <flow tuple>)."""
    if STATE["phase"] == "waiting":
        return False, "A sign-in is already waiting for the browser"
    if not host.online_allowed():  # uniform hook — always True on C4D today
        return False, "Online access is disabled"
    _stop.clear()
    STATE.update(phase="waiting", message="Contacting the site…")
    site = host.site_url()
    events.spawn(lambda: host.login_flow(site, lambda item: events.push("login", item), _stop.is_set))
    return True, ""


def cancel_login() -> None:
    _stop.set()


def handle_login_event(item: tuple) -> None:
    """Main-thread half of the flow — mutates STATE/VERDICT/credential store.

    The dialog calls this from its CoreMessage drain, then redraws. Browser
    opening stays with the caller (it owns the GUI thread).
    """
    kind = item[0]
    if kind == "open_url":
        STATE.update(phase="waiting", message="Authorize in your browser…")
    elif kind == "token":
        payload = item[1]
        config.save_credential(host.site_url(), payload["token"], name=payload.get("name"))
        set_verdict(payload.get("verdict"))
        STATE.update(phase="idle", message="")
    elif kind == "cancelled":
        STATE.update(phase="idle", message="")
    else:  # ("error", message)
        STATE.update(phase="error", message=item[1])


def refresh_verdict_async() -> None:
    """Re-validate the stored credential off-thread; pushes ("verdict", …)."""
    if not host.online_allowed():
        return
    site = host.site_url()
    credential = config.load_credential(site)
    if not credential:
        set_verdict(None)
        return

    def worker():
        try:
            status, data = host.validate(site, credential["token"])
            set_verdict(data if status == 200 else None)
            # Heal a nameless stored credential (e.g. saved while validate was
            # unreachable): the card and CLI then have a name even offline.
            display = api.display_name(VERDICT)
            if display and not credential.get("name"):
                config.save_credential(site, credential["token"], name=display)
        except Exception:
            set_verdict(None)  # explicit refresh failed; installed tools stay local
        events.push("verdict")

    events.spawn(worker)


def reset() -> None:
    _stop.set()
    STATE.update(phase="idle", message="")
    set_verdict(None)
